package org.jeecg.common.util.encryption;

/**
 * AES 128bit 加密解密工具类
 * @author wmf
 */

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


public class MyAesEncryptUtil {

    public static Logger logger = LoggerFactory.getLogger(AesEncryptUtil.class);

    //使用AES-128-CBC加密模式，key需要为16位,key和iv可以相同！
    private static String KEY = "dufy20170329java";

    private static String IV = "dufy20170329java";


    /**
     * 加密方法
     * @param data  要加密的数据
     * @param key 加密key
     * @param iv 加密iv
     * @return 加密的结果
     * @throws Exception
     */
    public static String encrypt(String data, String key, String iv) throws Exception {
        try {

            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");//"算法/模式/补码方式"
            int blockSize = cipher.getBlockSize();

            byte[] dataBytes = data.getBytes();
            int plaintextLength = dataBytes.length;
            if (plaintextLength % blockSize != 0) {
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }

            byte[] plaintext = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);

            return new Base64().encodeToString(encrypted);

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 解密方法
     * @param data 要解密的数据
     * @param key  解密key
     * @param iv 解密iv
     * @return 解密的结果
     * @throws Exception
     */
    public static String desEncrypt(String data, String key, String iv) throws Exception {
        try {
            byte[] encrypted1 = new Base64().decode(data);

            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original);
            return originalString;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 使用默认的key和iv加密
     * @param data
     * @return
     * @throws Exception
     */
    public static String encrypt(String data) throws Exception {
        return encrypt(data, KEY, IV);
    }

    /**
     * 使用默认的key和iv解密
     * @param data
     * @return
     * @throws Exception
     */
    public static String desEncrypt(String data) throws Exception {
        return desEncrypt(data, KEY, IV);
    }

    /**
     *
     * @param data 要加密的字符串
     * @param key 私钥:
     *            AES固定格式为128/192/256 bits.即:16/24/32bytes。
     *            DES固定格式为128bits，即8bytes。
     *
     * @param iv 初始化向量参数
     *              AES 为16bytes.
     *              DES 为8bytes.
     * @return
     * @throws Exception
     */
    public static String encrypt_pkcs(String data, String key, String iv) throws Exception
    {
        try
        {
            /**
             *
             * AES和DES 一共有4种工作模式:
             *     1.电子密码本模式(ECB) -- 缺点是相同的明文加密成相同的密文，明文的规律带到密文。
             *     2.加密分组链接模式(CBC)
             *     3.加密反馈模式(CFB)
             *     4.输出反馈模式(OFB)四种模式
             *
             * PKCS5Padding: 填充方式
             *
             * 加密方式/工作模式/填充方式
             * DES/CBC/PKCS5Padding
             */
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            byte[] dataBytes = data.getBytes();

            //两个参数，第一个为私钥字节数组， 第二个为加密方式 AES或者DES
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            /**
             * 初始化，此方法可以采用三种方式，按服务器要求来添加。
             * (1)无第三个参数 --> iv
             * (2)第三个参数为SecureRandom random = new SecureRandom();中random对象，随机数。(AES不可采用这种方法)
             * (3)采用此代码中的IVParameterSpec --> 指定好了的
             *
             * 解密使用 DECRYPT_MODE 方式
             */
            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(dataBytes);

            return new sun.misc.BASE64Encoder().encode(encrypted);

        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }

    public static String desEncrypt_pkcs(String dataEncode, String key, String iv) throws Exception
    {
        try
        {
            //先用Base64解码
            byte[] encrypted1 = new sun.misc.BASE64Decoder().decodeBuffer(dataEncode);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            // 解密使用 DECRYPT_MODE 方式
            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original);
            return originalString;
        }
        catch (Exception e) {
            e.printStackTrace();
            logger.error("aes 解密失败：data["+dataEncode+"]",e);
            return null;
        }
    }


    /**
     * 测试
     */
    public static void main(String args[]) throws Exception {

        String test = "1234567890";

        String data = null;
        String key = "0000001234567890";
        String iv = "0000001234567890";

        String en = encrypt_pkcs(test, key, iv);
        String de = desEncrypt_pkcs(en, key, iv);

        System.out.println("明文: " + test);
        System.out.println("加密: " + en);
        System.out.println("解密: " + de);

        data = encrypt(test, key, iv);

        System.out.println(data);
        System.out.println(desEncrypt("tNvmebi0/35iDrGYmxwKqQ==", key, iv));
    }

}
